#include "sample_svp_nnie_software.h"

#include <time.h>

# include "efficientdet.h"

// 参数设置
static SAMPLE_SVP_NNIE_MODEL_S stEfficientModel = {0}; // 模型参数
static SAMPLE_SVP_NNIE_PARAM_S stEfficientNnieParam[2] = {0}; // NNIE参数
static SVP_SOFT_EfficientDet stEfficientSoftParam[2] = {0}; // soft infor

static pthread_mutex_t Efficient_stop_mutex; // stop信号 互斥锁
static pthread_mutex_t Efficient_busy_mutex; // 主函数读图信号.子线程唤醒,并把busy为置0
static pthread_cond_t Efficient_busy_cond;
static pthread_t Efficient_nnie_pThread[2]; // 线程

// 全局参数
static EFFICIENT_THREAD_PARA glb_thred_para[2] = {0}; // 线程参数
static HI_U8 *glb_JPGfile_pu8Buf = NULL;  // 存储jpg文件的malloc空间
static HI_U32 glb_u32VdecChnNum = 1; // vdec通道数
static VPSS_GRP glb_VpssGrp; // vpss 组数 用于断开vpss
static HI_BOOL glb_abChnEnable[VPSS_MAX_CHN_NUM]; // VPSS 通道使能 标志位
static HI_S32 glb_s32VpssGrp = 0; // vpss group号 设置
static HI_S32 glb_as32VpssChn[] = {VPSS_CHN0, VPSS_CHN1}; // vpss 通道号 设置
static VIDEO_FRAME_INFO_S glb_stDetectInfo; // 检测图像
static HI_BOOL done_ctrl_c_flag = HI_FALSE; // 防止多线中多次 ctrl c触发释放内存程序
static int glb_img_count = 0;
static HI_CHAR glb_cStreamFile[1023]; // 用来存储jpg的名字

/* *****************************************************

* 线程函数

******************************************************* */
// 检测线程
static HI_VOID * EfficientDet_Det_pthread(HI_VOID *pArgs){
    HI_S32 s32Ret = HI_SUCCESS;
    EFFICIENT_THREAD_PARA *pThreadParam = (EFFICIENT_THREAD_PARA *)pArgs; // 函数输入

    SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam = pThreadParam->pstNnieParam; // NNIE信息
	SVP_SOFT_EfficientDet *pstSoftParam = pThreadParam->pstSoftParam; // 软件解码信息
    SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S *pstProcSegIdx = &pThreadParam->stProcSegIdx; // NNIE处理段id
    SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S *pstInputDataIdx = &pThreadParam->stInputDataIdx; // NNIE输入数据位置

    pthread_mutex_t *pbusy_mutex = &pThreadParam->busy_mutex;
	pthread_cond_t *pbusy_cond = &pThreadParam->busy_cond;

    HI_BOOL stop_flag = HI_FALSE;
    int img_count = 0;
    HI_CHAR cStreamFile[127]; // 用来存储jpg的名字
    while(1){
        // ***************       等待任务       ***************
        pthread_mutex_lock(pbusy_mutex);
        while(!pstSoftParam->pthread_busy_flag){
            // printf("nnie %d is free\n", pThreadParam->thread_id);
            pthread_cond_wait(pbusy_cond, pbusy_mutex); // 等待条件唤醒
        }
        img_count = glb_img_count;
        strcpy(cStreamFile, glb_cStreamFile);
        pthread_mutex_unlock(pbusy_mutex);
        
        // ***************       是否停止任务1       ***************
        pthread_mutex_lock(&Efficient_stop_mutex);
        stop_flag = pstSoftParam->pthread_stop_flag;
        pthread_mutex_unlock(&Efficient_stop_mutex);
        if(stop_flag) return (HI_VOID *)HI_FAILURE;

        // ***************       NNIE forward       ***************
        pstProcSegIdx->u32SegIdx = 0;
        s32Ret = EfficientDet_NNIE_Forward(pstNnieParam, pstInputDataIdx, pstProcSegIdx, HI_TRUE);
        if(HI_SUCCESS != s32Ret){
            printf("Error!! NNIE Forward , code:%d\n", s32Ret);
            goto DETECT_RELEASE;
        }

        // 创建txt
        char result_file_name[100];
        sprintf(result_file_name, EFFICIENTDET_SAVE_NAME, img_count);
        pstSoftParam->fp_result = fopen(result_file_name, "w"); // 创建bbox文件   sudo chown ccc bbox_result.txt
        // ***************       soft forward       ***************
        s32Ret = EfficientDet_Soft(pstNnieParam, pstSoftParam);
        if(HI_SUCCESS != s32Ret){
            printf("Error!! soft Forward , code:%d\n", s32Ret);
            goto DETECT_RELEASE;
        }
        
        // ***************       打印       ***************
        HI_FLOAT* final_result_tmp = (HI_FLOAT*)pstSoftParam->ps32FinalBbox.u64VirAddr;
        fprintf(pstSoftParam->fp_result, "%s\n", cStreamFile);
        for(HI_U32 ii=0; ii<pstSoftParam->final_detection_num; ++ii){ // [score, class, x1, y1, x2, y2]
            if(!EFFICIENTDET_PRINT_FLAG)
                printf("   score: %.4f   class: %d   xyxy: %.4f  %.4f  %.4f  %.4f\n",  
                    final_result_tmp[ii*6], (HI_S32)final_result_tmp[ii*6+1], final_result_tmp[ii*6+2], 
                    final_result_tmp[ii*6+3], final_result_tmp[ii*6+4], final_result_tmp[ii*6+5]);
            fprintf(pstSoftParam->fp_result, "%.4f %d %.1f %.1f %.1f %.1f\n", final_result_tmp[ii*6], (HI_S32)final_result_tmp[ii*6+1], 
                    final_result_tmp[ii*6+2], final_result_tmp[ii*6+3], final_result_tmp[ii*6+4], final_result_tmp[ii*6+5]); 
        }

        // ***************       清空任务       ***************
    DETECT_RELEASE:
        pthread_mutex_lock(&Efficient_busy_mutex);
        pthread_mutex_lock(pbusy_mutex);
        printf("nnie %d task finish\n", pThreadParam->thread_id);
        pstSoftParam->pthread_busy_flag = HI_FALSE;
        pthread_cond_signal(&Efficient_busy_cond); // 唤醒主线程
        pthread_mutex_unlock(pbusy_mutex);
        pthread_mutex_unlock(&Efficient_busy_mutex);

        // ***************       是否停止任务2       ***************
        pthread_mutex_lock(&Efficient_stop_mutex);
        stop_flag = pstSoftParam->pthread_stop_flag;
        pthread_mutex_unlock(&Efficient_stop_mutex);
        if(stop_flag) return (HI_VOID *)HI_FAILURE;
    }
    return (HI_VOID *)HI_SUCCESS;
}


// 开启2个检测线程
static HI_VOID EfficientDet_Start_Det_pthread()
{    
    pthread_mutex_init(&Efficient_stop_mutex, NULL);
    pthread_mutex_init(&Efficient_busy_mutex, NULL);
    pthread_cond_init(&Efficient_busy_cond, NULL);
    for(HI_S32 ii=0; ii<2; ++ii){
        Efficient_nnie_pThread[ii] = 0;
        stEfficientSoftParam[ii].pthread_stop_flag = HI_FALSE;
        stEfficientSoftParam[ii].pthread_busy_flag = HI_FALSE;
        //创建线程
        glb_thred_para[ii].pstNnieParam = &stEfficientNnieParam[ii];
        glb_thred_para[ii].pstSoftParam = &stEfficientSoftParam[ii];
        glb_thred_para[ii].thread_id = ii;
        pthread_mutex_init(&glb_thred_para[ii].busy_mutex, NULL);
        pthread_cond_init(&glb_thred_para[ii].busy_cond, NULL);
        pthread_create(&Efficient_nnie_pThread[ii], 0, EfficientDet_Det_pthread, (HI_VOID *)&glb_thred_para[ii]);
    }
}


// 关闭线程
static HI_VOID EfficientDet_Stop_Det_pthread()
{   
    // 修改停止标志位
    pthread_mutex_lock(&Efficient_stop_mutex);
    stEfficientSoftParam[0].pthread_stop_flag = HI_TRUE; // 停止标志位
    stEfficientSoftParam[1].pthread_stop_flag = HI_TRUE; 
    pthread_mutex_unlock(&Efficient_stop_mutex);

    for(HI_S32 ii=0; ii<2; ++ii){
        // 唤起条件变量,防止子线程卡住
        pthread_mutex_lock(&glb_thred_para[ii].busy_mutex);
        stEfficientSoftParam[ii].pthread_busy_flag = HI_TRUE; // 必须要这个跳出循环
        pthread_cond_signal(&glb_thred_para[ii].busy_cond);
        pthread_mutex_unlock(&glb_thred_para[ii].busy_mutex);

        usleep(100000); // 必须要延迟,否则参数会卡在里面
        // 关闭线程
        pthread_join(Efficient_nnie_pThread[ii], HI_NULL); // 阻塞方式等待线程执行完毕,回收线程资源
        Efficient_nnie_pThread[ii] = 0;
        // 销毁锁
        pthread_mutex_destroy(&glb_thred_para[ii].busy_mutex);
        pthread_cond_destroy(&glb_thred_para[ii].busy_cond);
    }
    pthread_mutex_destroy(&Efficient_busy_mutex);
    pthread_cond_destroy(&Efficient_busy_cond);
    pthread_mutex_destroy(&Efficient_stop_mutex);
}



/* *****************************************************

* jpg -> txt 退出函数

******************************************************* */
static void EfficientDet_Exit_2NNIE(int end_select){
    HI_S32 ii;
    HI_S32 s32Ret = HI_SUCCESS;
    done_ctrl_c_flag = HI_TRUE;

    if(glb_JPGfile_pu8Buf != NULL){  // 清理存储jpg文件的malloc内存
        free(glb_JPGfile_pu8Buf);
        glb_JPGfile_pu8Buf = NULL;
    }
    
    switch (end_select){
        case 1:
            goto END1; // 退出sys+vb初始化
        case 2:
            goto END2; // 退出vdec设置的vb
        case 3:
            goto END3; // 停止vdec
        case 4:
            goto END4; // 关闭vpss
        case 5:
            goto END5; // 断开 vpss vdec
        case 6:
            goto Model_END6; // 模型参数内存释放
        case 7:
            goto Model_END7; // NNIE0 硬件释放
        case 8:
            goto Model_END8; // NNIE1 硬件释放
        case 9:
            goto Model_END9; // NNIE0 释放soft
        case 10:
            goto Model_END10; // NNIE1 释放soft
        case 11:
            goto END11; // 跳出线程
        case 12:
            goto END12; // 释放VPSS通道的数据
        
        default:
            break;
    }

END12: // 释放VPSS通道的数据
    s32Ret = HI_MPI_VPSS_ReleaseChnFrame(glb_s32VpssGrp, glb_as32VpssChn[0], &glb_stDetectInfo);
    if (HI_SUCCESS != s32Ret)
        printf("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame1 failed,Grp(%d) chn(%d)!\n", s32Ret, glb_s32VpssGrp, glb_as32VpssChn[0]);

END11: // 跳出线程
    printf("stop pthread\n");
    EfficientDet_Stop_Det_pthread();

Model_END10: // NNIE1 释放soft
    Efficient_Soft_Deinit(&stEfficientSoftParam[1]);

Model_END9: // NNIE0 释放soft
    Efficient_Soft_Deinit(&stEfficientSoftParam[0]);

Model_END8: // NNIE1硬件释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(&stEfficientNnieParam[1]);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
    memset(&stEfficientNnieParam[1], 0, sizeof(SAMPLE_SVP_NNIE_PARAM_S));

Model_END7: // NNIE0硬件释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(&stEfficientNnieParam[0]);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
    memset(&stEfficientNnieParam[0], 0, sizeof(SAMPLE_SVP_NNIE_PARAM_S));

Model_END6: // 模型参数内存释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_UnloadModel(&stEfficientModel);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_UnloadModel failed!\n");
    memset(&stEfficientModel, 0, sizeof(SAMPLE_SVP_NNIE_MODEL_S));

END5: // 断开 vpss vdec
    for(ii=0; ii<glb_u32VdecChnNum; ii++){
        s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(ii, ii); // 使vdec和vpss断连
        if(s32Ret != HI_SUCCESS) SAMPLE_PRT("vdec unbind vpss fail for %#x!\n", s32Ret);
    }

END4: // 关闭vpss
    for(ii=glb_VpssGrp; ii>=0; ii--){ // i不能为unsigned int型
        glb_VpssGrp = ii;
        SAMPLE_COMM_VPSS_Stop(glb_VpssGrp, &glb_abChnEnable[0]); // 关闭所有group的vpss
    }

END3: // 停止vdec
    SAMPLE_COMM_VDEC_Stop(glb_u32VdecChnNum);

END2: // 退出vdec设置的vb
    SAMPLE_COMM_VDEC_ExitVBPool();

END1: // 退出sys+vb初始化
    SAMPLE_COMM_SYS_Exit();

    exit(0);
}

// 释放内存
void EfficientDet_JPG2txt_2NNIE_HandleSig(){
    if (!done_ctrl_c_flag) EfficientDet_Exit_2NNIE(0);
}



/* *****************************************************

* jpg -> txt 2NNIE 主函数 

******************************************************* */

void EfficientDet_JPG2txt_2NNIE()
{
    // ls imgs | sed "s:^:./efficient/data/imgs/:" >> img_list.txt
    FILE *fp_list = fopen("./efficient/data/img_list.txt", "r");// 判断list是否为空
    if(fp_list == NULL){ // list不能为空
        printf("Error: no file\n"); 
        exit(1);
    } 
    HI_CHAR *pcModelName = "./efficient/data/efficient_red_car.wk";
    start_reckon_by_time(); // 开始计时

    // ***************       1 sys+vb init       ***************
    HI_S32 ii = 0;
    HI_S32 s32Ret = HI_SUCCESS;
    {
        VB_CONFIG_S stVbConfig;
        printf("step1: vb init...\n");
        memset(&stVbConfig, 0, sizeof(VB_CONFIG_S));
        stVbConfig.u32MaxPoolCnt             = 1; // 系统可容纳缓存池个数
        stVbConfig.astCommPool[0].u32BlkCnt  = 10*glb_u32VdecChnNum; // 每个缓存池的缓存块个数
        stVbConfig.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(1920, 1080,
                                               PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0); // 缓存块大小
                                                    
        s32Ret = SAMPLE_COMM_SYS_Init(&stVbConfig);
        if(s32Ret != HI_SUCCESS){
            SAMPLE_PRT("init sys fail for %#x!\n", s32Ret);
            EfficientDet_Exit_2NNIE(1);
        }
    }
    PCPrintTime("Sys init", 0, 1, 1);
    
    /********    2 vdec的vb init   *********/
    SAMPLE_VDEC_ATTR astSampleVdec[VDEC_MAX_CHN_NUM];
    {
        printf("step2: vdec vb init...\n");
        for(ii=0; ii<glb_u32VdecChnNum; ii++){ // 有一个vdec通道
            astSampleVdec[ii].enType                            = PT_JPEG;
            astSampleVdec[ii].u32Width                          = 3840;
            astSampleVdec[ii].u32Height                         = 2160;
            astSampleVdec[ii].enMode                            = VIDEO_MODE_FRAME;
            astSampleVdec[ii].stSapmleVdecPicture.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
            astSampleVdec[ii].stSapmleVdecPicture.u32Alpha      = 255;
            astSampleVdec[ii].u32DisplayFrameNum                = 2;
            astSampleVdec[ii].u32FrameBufCnt                    = astSampleVdec[ii].u32DisplayFrameNum + 1;
        }
        s32Ret = SAMPLE_COMM_VDEC_InitVBPool(glb_u32VdecChnNum, &astSampleVdec[0]); // 根据类型、大小,设置公共视频缓存池属性(3个API)
        if(s32Ret != HI_SUCCESS){
            SAMPLE_PRT("init mod common vb fail for %#x!\n", s32Ret);
            EfficientDet_Exit_2NNIE(2);
        }
    }
    PCPrintTime("vdec vb init", 0, 1, 1);

    /********    3 开启vdec    *********/
    {
        printf("step3: start vdec...\n");
        s32Ret = SAMPLE_COMM_VDEC_Start(glb_u32VdecChnNum, &astSampleVdec[0]);
        if(s32Ret != HI_SUCCESS){
            SAMPLE_PRT("start VDEC fail for %#x!\n", s32Ret);
            EfficientDet_Exit_2NNIE(3);
        }
    }
    PCPrintTime("start vdec", 0, 1, 1);

    /********    4 开启vpss    *********/
    {
        printf("step4: start vpss...\n");
        // 用于创建vpss的属性
        VPSS_GRP_ATTR_S stVpssGrpAttr; // vpss组属性
        stVpssGrpAttr.u32MaxW = 3840; // 3840 输入图像
        stVpssGrpAttr.u32MaxH = 2160; // 2160
        stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1; // 组帧率
        stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
        stVpssGrpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8; // 输入图像动态范围
        stVpssGrpAttr.enPixelFormat  = PIXEL_FORMAT_YVU_SEMIPLANAR_420; // 输入图像像素格式
        stVpssGrpAttr.bNrEn   = HI_FALSE; // NR 使能开关
        // 给VPSS哪些通道使能 标志位
        memset(glb_abChnEnable, 0, sizeof(glb_abChnEnable));

        VPSS_CHN_ATTR_S astVpssChnAttr[VPSS_MAX_CHN_NUM]; // vpss通道属性
        astVpssChnAttr[0].u32Width                    = EFFICIENTDET_INPUT_WIDTH; // 输出图片宽/高
        astVpssChnAttr[0].u32Height                   = EFFICIENTDET_INPUT_HEIGHT;
        for(ii = 0; ii < 1; ii++){
            glb_abChnEnable[ii] = HI_TRUE; // 第0个vpss通道使能控制位

            astVpssChnAttr[ii].enChnMode                   = VPSS_CHN_MODE_USER; // VPSS_CHN_MODE_AUTO
            astVpssChnAttr[ii].enCompressMode              = COMPRESS_MODE_NONE; // 目标图像压缩模式 COMPRESS_MODE_SEG
            astVpssChnAttr[ii].enDynamicRange              = DYNAMIC_RANGE_SDR8; // 目标图像动态范围
            astVpssChnAttr[ii].enPixelFormat               = PIXEL_FORMAT_YVU_SEMIPLANAR_420; // 目标图像像素格式
            astVpssChnAttr[ii].stFrameRate.s32SrcFrameRate = -1; // 帧率控制信息
            astVpssChnAttr[ii].stFrameRate.s32DstFrameRate = -1;
            astVpssChnAttr[ii].u32Depth                    = 1; // 用户获取通道图像的队列长度 0
            astVpssChnAttr[ii].bMirror                     = HI_FALSE; // 水平镜像使能
            astVpssChnAttr[ii].bFlip                       = HI_FALSE; // 垂直翻转使能
            astVpssChnAttr[ii].stAspectRatio.enMode        = ASPECT_RATIO_NONE; // 幅形比参数
            astVpssChnAttr[ii].enVideoFormat               = VIDEO_FORMAT_LINEAR; // 目标图像视频格式
        }

        for(ii=0; ii<1; ii++){ // 1个通道 
            glb_VpssGrp = ii;
            s32Ret = SAMPLE_COMM_VPSS_Start(glb_VpssGrp, &glb_abChnEnable[0], &stVpssGrpAttr, &astVpssChnAttr[0]); // 开启vpss通道
            if(s32Ret != HI_SUCCESS){
                SAMPLE_PRT("start VPSS fail for %#x!\n", s32Ret);
                EfficientDet_Exit_2NNIE(4);
            }
        }      
    }
    PCPrintTime("start vpss", 0, 1, 1);

    /********    5 vdec连接vpss    *********/
    {
        printf("step5: vdec link vpss...\n");
        for(ii=0; ii<glb_u32VdecChnNum; ii++){
            s32Ret = SAMPLE_COMM_VDEC_Bind_VPSS(ii, ii);
            if(s32Ret != HI_SUCCESS){
                SAMPLE_PRT("vdec bind vpss fail for %#x!\n", s32Ret);
                EfficientDet_Exit_2NNIE(5);
            }
        }
        usleep(10);  
    }
    PCPrintTime("link vdec to vpss", 0, 1, 1);
          
    /********    6 发送数据到vdec    *********/
    VDEC_THREAD_PARAM_S stVdecSend[VDEC_MAX_CHN_NUM]; // vdec参数
    {
        printf("step6: set vdec para...\n");
        for(ii=0; ii<glb_u32VdecChnNum; ii++){ 
            snprintf(stVdecSend[ii].cFileName, sizeof(stVdecSend[ii].cFileName), "1080P.jpg");
            snprintf(stVdecSend[ii].cFilePath, sizeof(stVdecSend[ii].cFilePath), ".");
            stVdecSend[ii].enType          = astSampleVdec[ii].enType;
            stVdecSend[ii].s32StreamMode   = astSampleVdec[ii].enMode;
            stVdecSend[ii].s32ChnId        = ii;
            stVdecSend[ii].s32IntervalTime = 1000;
            stVdecSend[ii].u64PtsInit      = 0;
            stVdecSend[ii].u64PtsIncrease  = 0;
            stVdecSend[ii].eThreadCtrl     = THREAD_CTRL_START;
            stVdecSend[ii].bCircleSend     = HI_TRUE;
            stVdecSend[ii].s32MilliSec     = 0;
            stVdecSend[ii].s32MinBufSize   = (astSampleVdec[ii].u32Width * astSampleVdec[ii].u32Height * 3)>>1;
        } 
    }
    PCPrintTime("send data to vdec", 0, 1, 1);  

    /************************************************
    step: SVP
    *************************************************/

    // ***************       7 load wk       ***************
    {
        printf("step7: load model...\n");
        s32Ret = SAMPLE_COMM_SVP_NNIE_LoadModel(pcModelName, &stEfficientModel);
        if(s32Ret != HI_SUCCESS){
            SAMPLE_PRT("Error,NNIE_LoadModel failed for %#x!\n", s32Ret);
            EfficientDet_Exit_2NNIE(6);
        } 
    }
    PCPrintTime("load wk", 0, 1, 1);  
    
    // ***************       8 NNIE init       ***************
    {
        printf("step8: NNIE parameter init...\n");
        SAMPLE_SVP_NNIE_CFG_S   stNnieCfg[2] = {0};
        stNnieCfg[0].aenNnieCoreId[0] = SVP_NNIE_ID_0;
        stNnieCfg[1].aenNnieCoreId[0] = SVP_NNIE_ID_1;
        for(ii=0; ii<2; ++ii){
            stNnieCfg[ii].u32MaxInputNum = 1; // max input image num in each batch
            stNnieCfg[ii].u32MaxRoiNum = 0;
            stEfficientNnieParam[ii].pstModel = &stEfficientModel.stModel;
            s32Ret = SAMPLE_COMM_SVP_NNIE_ParamInit(&stNnieCfg[ii], &stEfficientNnieParam[ii]);
            if(s32Ret != HI_SUCCESS){
                SAMPLE_PRT("Error,NNIE_ParamInit failed for %#x!\n", s32Ret);
                EfficientDet_Exit_2NNIE(7+ii);
            } 
        }
    }
    PCPrintTime("NNIE init", 0, 1, 1);  

    // ***************       9 soft init       ***************'
    {
        printf("step9: soft parameter init...\n");
        for(ii=0; ii<2; ++ii){
            s32Ret = EfficientDet_Soft_ParamInit(&stEfficientSoftParam[ii]);
            if(s32Ret != HI_SUCCESS){
                SAMPLE_PRT("Error,NNIE_ParamInit failed for %#x!\n", s32Ret);
                EfficientDet_Exit_2NNIE(9+ii);
            }
        }
    }
    PCPrintTime("soft init", 0, 1, 1);

    // ***************       10 开启检测线程       ***************
    EfficientDet_Start_Det_pthread();
    PCPrintTime("start pthread", 1, 1, 1);
    
    
    // JPG存储空间
    HI_CHAR cStreamFile[127]; // 用来存储jpg的名字
    glb_JPGfile_pu8Buf = malloc(stVdecSend[0].s32MinBufSize); // 存储JPG原始数据
    if(glb_JPGfile_pu8Buf == NULL){
        printf("can't alloc %d in send stream thread!\n", stVdecSend[0].s32MinBufSize);
        EfficientDet_Exit_2NNIE(11);
    }
    fflush(stdout); // 标准输出流的清理 打印数据到屏幕上

    int img_count = 0;  // 检测图片数量
    start_reckon_by_time(); // 重新计时
    while(1)
    {
        // ***************       VDEC: JPG -> YUV       ***************
        VDEC_STREAM_S stStream; // 发送到vdec的数据流
        s32Ret = Efficient_Read_JPG(fp_list, glb_JPGfile_pu8Buf, cStreamFile, &stStream, stVdecSend); // 读取JPG,得到vdec
        if(HI_SUCCESS != s32Ret) EfficientDet_Exit_2NNIE(11); // 跳出循环
        img_count += 1;
        printf("\n%dth img name: %s\n", img_count, cStreamFile);

SendAgain: // 向视频解码通道发送码流数据 
        s32Ret=HI_MPI_VDEC_SendStream(stVdecSend[0].s32ChnId, &stStream, stVdecSend[0].s32MilliSec); // 0非阻塞
        if(HI_SUCCESS != s32Ret){ // 发送失败
            usleep(stVdecSend[0].s32IntervalTime);
            goto SendAgain;
        }
        
        // ***************       从VDEC获取bgr       ***************
        s32Ret = HI_MPI_VPSS_GetChnFrame(glb_s32VpssGrp, glb_as32VpssChn[0], &glb_stDetectInfo, 20); // 获取图像等待时间:20
        if(HI_SUCCESS != s32Ret){
            printf("Error(%#x),HI_MPI_VPSS_GetChnFrame failed, VPSS_GRP(%d), VPSS_CHN(%d)!\n", s32Ret,glb_s32VpssGrp, glb_as32VpssChn[0]);
            goto VPSS_RELEASE;
        }
        
        // ***************       等待NNIE空闲       ***************
        pthread_mutex_lock(&Efficient_busy_mutex);
        while(stEfficientSoftParam[0].pthread_busy_flag && stEfficientSoftParam[1].pthread_busy_flag){
            // printf("all nnie is busy\n");
            pthread_cond_wait(&Efficient_busy_cond, &Efficient_busy_mutex); // 等待条件唤醒
        }
        pthread_mutex_unlock(&Efficient_busy_mutex);

        // ***************       分配NNIE资源       ***************
        for(ii=0; ii<2; ++ii){
            pthread_mutex_lock(&glb_thred_para[ii].busy_mutex);
            if(!stEfficientSoftParam[ii].pthread_busy_flag){
                // printf("main wake pthread %d\n", ii);
                s32Ret = Efficient_YUV2BGR(&glb_stDetectInfo, &stEfficientNnieParam[ii], &glb_thred_para[ii].stInputDataIdx); // yuv -> bgr
                if(HI_SUCCESS == s32Ret){
                    glb_img_count = img_count;
                    strcpy(glb_cStreamFile, cStreamFile);
                    stEfficientSoftParam[ii].pthread_busy_flag = HI_TRUE;
                    pthread_cond_signal(&glb_thred_para[ii].busy_cond); // 唤醒子线程
                }
                pthread_mutex_unlock(&glb_thred_para[ii].busy_mutex);
                break;
            }
            pthread_mutex_unlock(&glb_thred_para[ii].busy_mutex);
        }
        if(HI_SUCCESS != s32Ret) goto VPSS_RELEASE;

    // 释放检测图片
    VPSS_RELEASE:
        s32Ret = HI_MPI_VPSS_ReleaseChnFrame(glb_s32VpssGrp, glb_as32VpssChn[0], &glb_stDetectInfo);
        if (HI_SUCCESS != s32Ret){
            printf("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n", s32Ret, glb_s32VpssGrp, glb_as32VpssChn[0]);
            EfficientDet_Exit_2NNIE(11);
        }
        Efficient_mean_time(img_count);
    }
    printf("task finish\n");
    EfficientDet_Exit_2NNIE(0);  // 退出程序
}

